#!/bin/bash

# 设置文件备份函数
mk_bak() {
    local file_path="${@: -1}"
    if [ -e "${file_path}" ]
    then
        cp -av "${file_path}" "${file_path}.$(date +%F_%s)"
    else
        echo "error not exist file ${file_path}"
        return 1
    fi
}

#######################################################
#  确保启用对在auditd之前启动的进程的审计
#######################################################

mk_bak "/etc/default/grub" &&
sed -i -r "\$aGRUB_CMDLINE_LINUX=\"audit=1\"" /etc/default/grub &&
grub2-mkconfig -o /boot/grub2/grub.cfg &&
echo "
1  确保启用对在auditd之前启动的进程的审计
"

#######################################################
#  确保收集了会话启动信息 %1-3
#  确保收集了系统管理员操作（sudolog）%13
#  确保审核配置是不变的 %16注释
#  确保收集了登录和注销事件 %4-5
#  确保系统管理范围（sudoers）更改的收集 %14-15
#  确保收集了修改用户/组信息的事件 %8-12
#  确保收集了修改系统的强制访问控制的事件 %6-7
#######################################################

audit_path='/etc/audit/rules.d/audit.rules'
typeset -a audit_cfg_v=(
    '-w /var/run/utmp -p wa -k session'
    '-w /var/log/wtmp -p wa -k logins'
    '-w /var/log/btmp -p wa -k logins'
    '-w /var/log/lastlog -p wa -k logins'
    '-w /var/run/faillock/ -p wa -k logins'
    '-w /etc/selinux/ -p wa -k MAC-policy'
    '-w /usr/share/selinux/ -p wa -k MAC-policy'
    '-w /etc/group -p wa -k identity'
    '-w /etc/passwd -p wa -k identity'
    '-w /etc/gshadow -p wa -k identity'
    '-w /etc/shadow -p wa -k identity'
    '-w /etc/security/opasswd -p wa -k identity'
    '-w /var/log/sudo.log -p wa -k actions'
    '-w /etc/sudoers -p wa -k scope'
    '-w /etc/sudoers.d/ -p wa -k scope'
#    '-e 2'
# 这条最后手动操作吧，跑完脚本验证没问题再执行这条
)

mk_bak "${audit_path}" &&
if grep -P "^\s*-e\s+2" "${audit_path}"
then
    echo "${audit_path} 存在 -e 2 需要手动确认是否能够修改成功"
    sed -i -r '/-e[ \t]*2/d' "${audit_path}"
fi 
# 开始写入
for value in "${audit_cfg_v[@]}"
do
    if ! grep -e "${value}" "${audit_path}"
    then
        sed -i -r "\$a${value}" "${audit_path}"
    fi
done &&
service auditd restart &&
echo "
2  确保收集了会话启动信息 %1-3
3  确保收集了系统管理员操作（sudolog）%13
4  确保审核配置是不变的 %16注释
5  确保收集了登录和注销事件 %4-5
6  确保系统管理范围（sudoers）更改的收集 %14-15
7  确保收集了修改用户/组信息的事件 %8-12
8  确保收集了修改系统的强制访问控制的事件 %6-7
"

#######################################################
#  确保SSH MaxAuthTries设置为4或更低
#  确保已禁用SSH空密码登录
#  确保已配置SSH空闲超时间隔
#######################################################

ssh_path='/etc/ssh/sshd_config'
typeset -A sshd_cfg_kv=(
    [MaxAuthTries]='4'
    [PermitEmptyPasswords]='no'
    [ClientAliveInterval]='300'
    [ClientAliveCountMax]='0'
)

mk_bak "${ssh_path}" &&
for key in "${!sshd_cfg_kv[@]}"
do
    value=${sshd_cfg_kv[$key]}
    if grep -iP "^\s*${key}" ${ssh_path}
    then
        # 注意：此处Ic表示忽略大小写匹配后替换原配置行
        sed -i -r "/^[ \t]*${key}/Ic${key} ${value} $(date +'#lhc_modify_setting@%F')" ${ssh_path}
    else
        sed -i -r "\$a${key} ${value} $(date +'#lhc_modify_setting@%F')" ${ssh_path}
    fi
done &&
sshd -t &&
systemctl restart sshd &&
echo "
9  确保SSH MaxAuthTries设置为4或更低
10  确保已禁用SSH空密码登录
11  确保已配置SSH空闲超时间隔
"
#######################################################
#  确保配置了密码尝试失败的锁定
#######################################################

mk_bak "/etc/pam.d/password-auth" &&
mk_bak "/etc/pam.d/system-auth" &&
if grep 'pam_faillock.so' /etc/pam.d/system-auth
then
    echo '已经修改过该项目，请手动查看数值是否正确'
else
    sed -i -r '/auth.*required.*pam_env.so/a\
auth required pam_tally2.so deny=5 onerr=fail unlock_time=900 \
auth required pam_faillock.so preauth audit silent deny=5 unlock_time=900 \
auth [success=1 default=bad] pam_unix.so \
auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 \
auth sufficient pam_faillock.so authsucc audit deny=5 unlock_time=900 ' /etc/pam.d/system-auth &&
    sed -i -r '/auth.*required.*pam_env.so/a\
auth required pam_tally2.so deny=5 onerr=fail unlock_time=900 \
auth required pam_faillock.so preauth audit silent deny=5 unlock_time=900 \
auth [success=1 default=bad] pam_unix.so \
auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 \
auth sufficient pam_faillock.so authsucc audit deny=5 unlock_time=900 ' /etc/pam.d/password-auth
fi &&
echo "
12  确保配置了密码尝试失败的锁定
"

#######################################################
#  确保已启用auditd服务
#######################################################

systemctl enable auditd &&
echo "
13 确保已启用auditd服务
"

#######################################################
#  确保审核日志不会自动删除
#######################################################

audit_cfg_path='/etc/audit/auditd.conf'
typeset -A audit_cfg_kv=(
    ['max_log_file_action']='keep_logs'
# 下边三行如果设置，会导致日志写满强制关机，影响业务
    # ['space_left_action']='email'
    # ['action_mail_acct']='root'
    # ['admin_space_left_action']='halt'
)

mk_bak "${audit_cfg_path}"  &&
for key in "${!audit_cfg_kv[@]}"
do
    value=${audit_cfg_kv[$key]}
    if grep -iP "^\s*${key}" ${audit_cfg_path}
    then
        # 注意：此处Ic表示忽略大小写匹配后替换原配置行
        sed -i -r "/^[ \t]*${key}/Ic${key} = ${value}" ${audit_cfg_path}
    else
        sed -i -r "\$a${key} = ${value}" "${audit_cfg_path}"
    fi
done &&
echo "
14  确保审核日志不会自动删除
"

#######################################################
#  确保默认用户shell超时为900秒或更短
#######################################################
tmout_path=(
    '/etc/bashrc'
    '/etc/profile'
)

for i in "${tmout_path[@]}"
do
    mk_bak "${i}" &&
    if grep -P "^\s*TMOUT" "${i}"
    then
        sed -i -r "/^[ \t]*TMOUT/cTMOUT=600" "${i}"
    else
        sed -i -r "\$aTMOUT=600" "${i}"
    fi  ||
    break
done &&
echo "
15  确保默认用户shell超时为900秒或更短
"

#######################################################
#  确保核心转储受到限制
#  确保启用了地址空间布局随机化（ASLR）
#######################################################

limits_path='/etc/security/limits.conf'
sysctl_path='/etc/sysctl.conf'
typeset -A sysctl_cfg_kv=(
    ['fs.suid_dumpable']='0'
    ['kernel.randomize_va_space']='2'
)

mk_bak "${limits_path}" &&
if grep -P -i "hard\s*core" "${limits_path}"
then
    sed -i -r "/hard[ \t]*core/c* hard core 0" "${limits_path}"
else
    sed -i -r "\$a* hard core 0" "${limits_path}"
fi &&
mk_bak ${sysctl_path} &&
for key in "${!sysctl_cfg_kv[@]}"
do
    value=${sysctl_cfg_kv[$key]}
    if grep -iP "^\s*${key}" ${sysctl_path}
    then
        # 注意：此处Ic表示忽略大小写匹配后替换原配置行
        sed -i -r "/^[ \t]*${key} *=/Ic${key} = ${value}" ${sysctl_path}
    else
        sed -i -r "\$a${key} = ${value}" ${sysctl_path}
    fi
    sysctl -w "${key}=${value}"
done &&
echo "
16 确保核心转储受到限制
17 确保启用了地址空间布局随机化（ASLR）
"

#######################################################
#  确保配置了bootloader配置的权限
#######################################################

chown root:root /boot/grub2/grub.cfg &&
chmod og-rwx /boot/grub2/grub.cfg &&
echo "
18 确保配置了bootloader配置的权限
"

#######################################################
#  确保默认用户umask限制为027或更高
#######################################################

typeset -a umask_path=(
    /etc/bashrc
    /etc/profile
)

for v in "${umask_path[@]}"
do
    mk_bak "${v}" &&
    if grep -P "^\s*umask" "${v}"
    then
        sed -i -r "/^[ \t]*umask/cumask 027" "${v}"
    else
        sed -i -r "\$aumask 027" "${v}"
    fi
done &&
echo "
19 确保默认用户umask限制为027或更高
"